﻿namespace EveKillboard.Core.Parsers
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Xml.XPath;
    using Services;

    public sealed class XmlFeedParser
    {
        private const int CARGO = 5;
        private const int DRONEBAY = 87;
        private const int IMPLANT = 89;
        private const int BP_COPY = 2;
        private const string NONE = "None";
        private const string UNKNOWN = "Unknown";

        private readonly TraceSource _trace = new TraceSource("XmlFeedParser");

        private IList<IFeedKillmail> _killmails;

        public XmlFeedParser() { }

        public IEnumerable<IFeedKillmail>  Killmails
        {
            get { return _killmails; }
        }

        public bool HasError { get { return ErrorCode != 0; } }

        public string ErrorMessage { get; set; }

        public int ErrorCode { get; set; }

        public void Parse(Stream stream)
        {
            _trace.TraceEvent(TraceEventType.Verbose, 0, "Parsing Xml content");

            ErrorMessage = null;
            ErrorCode = 0;

            if(_killmails == null)
                _killmails = new List<IFeedKillmail>();

            if(_killmails.Any())
                _killmails.Clear();

            var nav = new XPathDocument(stream).CreateNavigator();

            var error = nav.SelectSingleNode("/eveapi/error");
            if(error !=null && !string.IsNullOrEmpty(error.GetAttribute("code","")))
            {
                ErrorCode = int.Parse(error.GetAttribute("code", ""));
                ErrorMessage = error.InnerXml;

                _trace.TraceEvent(TraceEventType.Verbose, 0, "The request returned Error Code: {0}. Message: {1}", ErrorCode, ErrorMessage);
                return ;
            }

            var nodes = nav.Select("/eveapi/result/rowset[@name=\"kills\"]/row");
            while (nodes.MoveNext())
                if (nodes.Current != null)
                    try
                    {
                        var kill = GetKillmail(nodes.Current);
                        if (kill != null)
                            _killmails.Add(kill);
                    }
                    catch (Exception exp)
                    {
                        _trace.TraceEvent(TraceEventType.Error, 0, "The parsing of current node has thrown an exception of type {0}. Message: {1}. Content: {2}", exp.GetType(), exp.Message, nodes.Current.OuterXml);
                    }

            _trace.TraceEvent(TraceEventType.Verbose, 0, "Parsing Xml finished");
        }

        private static IFeedKillmail GetKillmail(XPathNavigator nav)
        {
            if (nav == null) return null;

            var victim = nav.SelectSingleNode("victim");
            if (victim == null) return null;

            var kill = new FeedKillmail()
                           {
                               CcpId = int.Parse(GetAttributeValue(nav, "killID")),
                               InternalId = int.Parse(GetAttributeValue(nav, "killID")),
                               Date = DateTime.ParseExact(GetAttributeValue(nav, "killTime"), "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture),
                               DamageReceived = int.Parse(GetAttributeValue(victim, "damageTaken")),
                               SystemId = int.Parse(GetAttributeValue(nav, "solarSystemID")),
                               VictimId = int.Parse(GetAttributeValue(victim, "characterID")),
                               VictimName = GetAttributeValue(victim, "characterName"),
                               VictimCorporationName = ClearName( GetAttributeValue(victim, "corporationName") ),
                               VictimCorporationId = int.Parse(GetAttributeValue(victim, "corporationID")),
                               VictimAllianceName = ClearName(GetAttributeValue(victim, "allianceName")),
                               VictimAllianceId = int.Parse(GetAttributeValue(victim, "allianceID")),
                               VictimFactionName = ClearName(GetAttributeValue(victim, "factionName")),
                               VictimFactionId = int.Parse(GetAttributeValue(victim, "factionID")),
                               VictimShipId = int.Parse(GetAttributeValue(victim, "shipTypeID")),
                               Involved = new List<IFeedKillmailInvolved>(),
                               Items = new List<IFeedKillmailItem>()
                           };

            var involved = nav.Select("rowset[@name=\"attackers\"]/row");
            while(involved.MoveNext())
                if(involved.Current !=null)
                {
                    ((IList) kill.Involved).Add(new FeedKillmailInvolved()
                                                    {
                                                        CharacterId = int.Parse(GetAttributeValue(involved.Current, "characterID")),
                                                        CharacterName = GetAttributeValue(involved.Current, "characterName"),
                                                        CorporationId = int.Parse(GetAttributeValue(involved.Current, "corporationID")),
                                                        CorporationName = ClearName(GetAttributeValue(involved.Current, "corporationName")),
                                                        AllianceId = int.Parse(GetAttributeValue(involved.Current, "allianceID")),
                                                        AllianceName = ClearName(GetAttributeValue(involved.Current, "allianceName")),
                                                        FactionId = int.Parse(GetAttributeValue(involved.Current, "factionID")),
                                                        FactionName = ClearName(GetAttributeValue(involved.Current, "factionName")),
                                                        SecurityStatus = float.Parse(GetAttributeValue(involved.Current, "securityStatus"), CultureInfo.InvariantCulture),
                                                        DamageDone = int.Parse(GetAttributeValue(involved.Current, "damageDone")),
                                                        IsFinalBlow = int.Parse(GetAttributeValue(involved.Current, "finalBlow")) == 1,
                                                        ShipId = int.Parse(GetAttributeValue(involved.Current, "shipTypeID")),
                                                        WeaponId = int.Parse(GetAttributeValue(involved.Current, "weaponTypeID"))
                                                    });
                }

            var items = nav.Select("rowset[@name=\"items\"]/row");
            while(items.MoveNext())
                if(items.Current !=null)
                {
                    if(int.Parse(GetAttributeValue(items.Current, "qtyDropped")) > 0)
                    {
                        ((IList) kill.Items).Add(new FeedKillmailItem()
                                                     {
                                                         IsDestroyed = false,
                                                         IsDropped = true,
                                                         ItemId = int.Parse(GetAttributeValue(items.Current, "typeID")),
                                                         Quantity = int.Parse(GetAttributeValue(items.Current, "qtyDropped")),
                                                         IsCargo = int.Parse(GetAttributeValue(items.Current, "flag")) == CARGO,
                                                         IsDroneBay = int.Parse(GetAttributeValue(items.Current, "flag")) == DRONEBAY,
                                                         IsImplant = int.Parse(GetAttributeValue(items.Current, "flag")) == IMPLANT,
                                                         IsCopy = int.Parse(GetAttributeValue(items.Current, "singleton")) == BP_COPY
                                                     });
                    }
                    if (int.Parse(GetAttributeValue(items.Current, "qtyDestroyed")) > 0)
                    {
                        ((IList) kill.Items).Add(new FeedKillmailItem()
                                                     {
                                                         IsDestroyed = true,
                                                         IsDropped = false,
                                                         ItemId = int.Parse(GetAttributeValue(items.Current, "typeID")),
                                                         Quantity = int.Parse(GetAttributeValue(items.Current, "qtyDestroyed")),
                                                         IsCargo = int.Parse(GetAttributeValue(items.Current, "flag")) == CARGO,
                                                         IsDroneBay = int.Parse(GetAttributeValue(items.Current, "flag")) == DRONEBAY,
                                                         IsImplant = int.Parse(GetAttributeValue(items.Current, "flag")) == IMPLANT,
                                                         IsCopy = int.Parse(GetAttributeValue(items.Current, "singleton")) == BP_COPY
                                                     });
                    }

                }
            
            return kill;
        }

        private static string GetAttributeValue(XPathNavigator nav, string attr)
        {
            return nav.HasAttributes && !string.IsNullOrEmpty(nav.GetAttribute(attr, ""))
                       ? nav.GetAttribute(attr, "")
                       : null;
        }

        private static string ClearName(string name)
        {
            var temp = string.IsNullOrEmpty(name)
                       ? null
                       : name.Replace(NONE, null).Replace(UNKNOWN, null).Trim();

            return string.IsNullOrEmpty(temp) ? null : temp;
        }
    }
}